package com.lagou.edu;

import com.lagou.edu.annotation.MyAutowired;
import com.lagou.edu.annotation.MyService;
import com.lagou.edu.annotation.MyTransactional;
import com.lagou.edu.factory.ProxyFactory;
import com.lagou.edu.utils.CharsetUtils;
import org.apache.commons.lang3.StringUtils;
import org.reflections.Reflections;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author liJin
 * @date 2020/12/9 0:30
 * @desc
 */
public class WebApplicationContext {
    private static final String ROOT = "com.lagou.edu";
    private static final ConcurrentHashMap<String, Object> beanMap = new ConcurrentHashMap<>(256);

    static {
        try {
            initBeanFactory();
            refreshProperties();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 扫描包, 获取实例化的对象, 存储到Map中
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    public static void initBeanFactory() throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        Reflections reflections = new Reflections(ROOT);
        Set<Class<?>> serviceClazz = reflections.getTypesAnnotatedWith(MyService.class);
        for (Class<?> clazz : serviceClazz) {
            Object o = clazz.getDeclaredConstructor().newInstance();
            // 没有value则用类名作为key
            MyService annotation = clazz.getAnnotation(MyService.class);
            String key = annotation.value();
            if(StringUtils.isEmpty(key)){
                String[] split = clazz.getName().split("\\.");
                key = CharsetUtils.toLowerCase(split[split.length - 1]);
            }
            registerBean(key, o);
        }
    }

    /**
     * 处理单例池对象中的属性依赖
     * @throws IllegalAccessException
     * @throws NoSuchFieldException
     */
    public static void refreshProperties() throws IllegalAccessException, NoSuchFieldException {
        for (Map.Entry<String, Object> bean : beanMap.entrySet()) {
            Object o = bean.getValue();
            Class clazz = o.getClass();
            // 获取所有属性
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                if (field.isAnnotationPresent(MyAutowired.class)) {
                    // 获取依赖属性对应的名称
                    String[] names = field.getType().getName().split("\\.");
                    String name = CharsetUtils.toLowerCase(names[names.length - 1]);
                    // 给指定属性赋值
                    Field declaredField = clazz.getDeclaredField(name);
                    declaredField.setAccessible(true);
                    declaredField.set(o, beanMap.get(name));
                }
            }
            // 处理Transactional注解
            if(clazz.isAnnotationPresent(MyTransactional.class)){
                ProxyFactory proxyFactory = (ProxyFactory) getBean("proxyFactory");
                //判断对象是否实现接口
                Class[] interfaces = clazz.getInterfaces();
                o = (interfaces != null && interfaces.length > 0) ? proxyFactory.getJdkProxy(o) : proxyFactory.getCglibProxy(o);
            }
            registerBean(bean.getKey(), o);
        }
    }

    public static void registerBean(String name, Object beanDefinition){
        beanMap.put(name, beanDefinition);
    }

    public static Object getBean(String name){
        Object bean = beanMap.get(name);
        if(bean == null){
            throw new RuntimeException("No bean named '" + name + "' available");
        }
        return bean;
    }
}
